package cc.comac.mom.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import lombok.Data;
import lombok.NonNull;

abstract class BaseService {

    private Validator validator = Validation.buildDefaultValidatorFactory()
            .getValidator();


    @Autowired
    protected ExportUtil exportUtil;
    @Autowired
    protected ImportUtil importUtil;

    protected void copyPropertyWithoutLogInfo(@NonNull Object src, @NonNull Object target) {
        BeanUtils.copyProperties(src, target, "id", "version", "creator", "creationDate", "lsdModifier", "modifiedDate");
    }



    protected <T> String validate(@NonNull T obj) {
        String result = validate(obj, null);
        if (StringUtils.isNotEmpty(result)) {
            return result;
        }
        return validateDictData(Arrays.asList(obj));
    }

    private <T> String validate(@NonNull T obj, Integer rowIndex) {
        Set<ConstraintViolation<T>> set = validator.validate(obj);
        return convertErrorMap(set).entrySet().stream().map(e -> {
            return (rowIndex == null ? "" : "第" + rowIndex + "行数据校验有问题,") + "属性" + e.getKey() + "校验出错: " + e.getValue();
        }).collect(Collectors.joining(";"));
    }

    protected <T> String validate(@NonNull List<T> objs) {
        List<String> result = new ArrayList<>();
        for (int i = 0; i < objs.size(); i++) {
            String vr = validate(objs.get(i), objs.size() > 1 ? new Integer(i) : null);
            if (StringUtils.isNotEmpty(vr)) {
                result.add(vr);
            }
        }
        if (result.size() > 0) {
            return result.stream().collect(Collectors.joining("\n"));
        }
        return validateDictData(objs);
    }

    private <T> Map<String, String> convertErrorMap(Set<ConstraintViolation<T>> set) {
        Map<String, StringBuilder> errorMap = new HashMap<>();
        if (set != null && set.size() > 0) {
            String property = null;
            for (ConstraintViolation<T> cv : set) {
                property = cv.getPropertyPath().toString();
                if (errorMap.get(property) != null) {
                    errorMap.get(property).append("," + cv.getMessage());
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(cv.getMessage());
                    errorMap.put(property, sb);
                }
            }
        }
        return errorMap.entrySet().stream().collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue().toString()));
    }

    private <T> String validateDictData(@NonNull List<T> data) {
        return null;
    }





    protected <T> XSSFWorkbook download(String sheetName, Class<T> clz) {
        List<Field> fields = ReflectionUtil.getFieldAnnotatedWith(clz, ExcelColumn.class, "template");
        List<String> columnNames = fields.stream().map(field -> field.getAnnotation(ExcelColumn.class).text()).collect(Collectors.toList());
        List<String> requiredList = columnNames;
        Map<Integer, List<String>> optionsMap = new HashMap<>();
        XSSFWorkbook workbook = new XSSFWorkbook();
        return exportUtil.executeSheet(workbook, sheetName, "", columnNames, requiredList, new ArrayList<>(), new ArrayList<>(), optionsMap, false);
    }

    protected <PO> PO findExisting(PO coming, BaseMapper<PO> mapper) {
        return findExisting(coming, null, mapper);
    }

    protected <PO> PO findExisting(PO coming, String condition, BaseMapper<PO> mapper) {
        List<Field> fields = ReflectionUtil.getFieldAnnotatedWithBusinessKey(coming.getClass(), condition);
        Map<String, Object> searchCriteria = new HashMap<>();
        fields.forEach(field -> {
            String columnName = field.getAnnotation(BusinessKey.class).dbField();
            Object value = ReflectionUtil.getFieldValueSliently(coming, field.getName());
            searchCriteria.put(columnName, value);
        });
        List<PO> result = mapper.selectByMap(searchCriteria);
        //it is possible the business key is not unique? dirty data, so just query through list
        return result.size() > 0 ? result.get(0) : null;
    }
    @Data
    public static class DictionaryDTO {
        private String code;
        private String display;
    }
}
